// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此文件实现多精度十进制数。
// 此实现仅用于浮点到十进制的转换；
// 非通用用途。
// 唯一的操作是从二进制到
// 十进制和四舍五入的精确转换。
// 
// 关键观察值和一些代码（shr）借用自
// /strconv/decimal.go：二进制分式值的转换可以在多精度十进制中精确完成，因为2除以10（对于尾数的
// />是必需的）；但是十进制浮点值的转换不能精确地用二进制表示。
// 
// 与strconv/decimal.go相比，只有右移位是以十进制格式实现的——左移位可以以二进制格式精确实现。

package big

// 十进制表示无符号浮点数。
// 非零十进制d的值为d.mant*10**d.exp，0.1<=d.mant<1，
// 最有效的尾数数字在索引0处。对于零十进制，
// 尾数长度和指数为0。
// 十进制的零值表示可随时使用的0.0。
type decimal struct {
	mant []byte // 尾数ASCII数字，大端
	exp  int    // 指数
}

// at返回第i个尾数数字，从最高有效位0开始。
func (d *decimal) at(i int) byte {
	if 0 <= i && i < len(d.mant) {
		return d.mant[i]
	}
	return '0'
}

// 一次可完成且无溢出的最大移位量。
// 一个字有_W个位，并且（1<<maxShift-1）*10+9必须适合该字。
const maxShift = _W - 4

// TODO（gri）由于我们在转换浮点数时知道所需的十进制精度，我们可以通过提供一个额外的
// 位数（相当于二进制舍入中的“粘性位”）。
// 精度参数并跟踪一个数字何时被截断，从而限制需要由init计算的十进制

// TODO（gri）沿着相同的路线，强制执行一些限制来移动量级
// ，以避免“无限”长时间运行的转换（直到空间耗尽）。

// Init将x初始化为m<shift（对于
// shift>=0）或m>-shift（对于shift<0）的十进制表示形式。
func (x *decimal) init(m nat, shift int) {
	// 特例0 
	if len(m) == 0 {
		x.mant = x.mant[:0]
		x.exp = 0
		return
	}

	// 优化：如果我们需要右移，首先从m中删除任何尾随的
	// 零位，以减少需要以
	// 十进制格式执行的移位量（因为这可能会更慢）。
	if shift < 0 {
		ntz := m.trailingZeroBits()
		s := uint(-shift)
		if s >= ntz {
			s = ntz // 最多移位ntz位
		}
		m = nat(nil).shr(m, s)
		shift += int(s)
	}

	// 在二进制表示中向左移位。
	if shift > 0 {
		m = nat(nil).shl(m, uint(shift))
		shift = 0
	}

	// 将尾数转换为十进制表示。
	s := m.utoa(10)
	n := len(s)
	x.exp = n
	// 修剪尾随零；相反，指数跟踪的是
	// 小数点，与位数无关。
	for n > 0 && s[n-1] == '0' {
		n--
	}
	x.mant = append(x.mant[:0], s[:n]...)

	// 在十进制表示法中进行任何（剩余）右移。
	if shift < 0 {
		for shift < -maxShift {
			shr(x, maxShift)
			shift += maxShift
		}
		shr(x, uint(-shift))
	}
}

// shr实现x>>s，对于s<=maxShift。
func shr(x *decimal, s uint) {
	// 使用移位和减法算法除以1<<s。

	// 选取足够的前导数字以覆盖第一个移位
	r := 0 // 读取索引
	var n Word
	for n>>s == 0 && r < len(x.mant) {
		ch := Word(x.mant[r])
		r++
		n = n*10 + ch - '0'
	}
	if n == 0 {
		// x==0；不应该到这里，但无论如何要处理
		x.mant = x.mant[:0]
		return
	}
	for n>>s == 0 {
		r++
		n *= 10
	}
	x.exp += 1 - r

	// 读一个数字，写一个数字
	w := 0 // 写索引
	mask := Word(1)<<s - 1
	for r < len(x.mant) {
		ch := Word(x.mant[r])
		r++
		d := n >> s
		n &= mask // n-=d<<s 
		x.mant[w] = byte(d + '0')
		w++
		n = n*10 + ch - '0'
	}

	// 写仍然适合
	for n > 0 && w < len(x.mant) {
		d := n >> s
		n &= mask
		x.mant[w] = byte(d + '0')
		w++
		n = n * 10
	}
	x.mant = x.mant[:w] // 数字可能更短（例如1024>>10）

	// 附加不符合
	for n > 0 {
		d := n >> s
		n &= mask
		x.mant = append(x.mant, byte(d+'0'))
		n = n * 10
	}

	trim(x)
}

func (x *decimal) String() string {
	if len(x.mant) == 0 {
		return "0"
	}

	var buf []byte
	switch {
	case x.exp <= 0:
		// 0.00ddd 
		buf = make([]byte, 0, 2+(-x.exp)+len(x.mant))
		buf = append(buf, "0."...)
		buf = appendZeros(buf, -x.exp)
		buf = append(buf, x.mant...)

	case /* 0 < */ x.exp < len(x.mant):
		// dd.ddd 
		buf = make([]byte, 0, 1+len(x.mant))
		buf = append(buf, x.mant[:x.exp]...)
		buf = append(buf, '.')
		buf = append(buf, x.mant[x.exp:]...)

	default: // len（x.mant）<=x.exp 
		// ddd00 
		buf = make([]byte, 0, x.exp)
		buf = append(buf, x.mant...)
		buf = appendZeros(buf, x.exp-len(x.mant))
	}

	return string(buf)
}

// 附加零将n 0位附加到buf并返回buf。
func appendZeros(buf []byte, n int) []byte {
	for ; n > 0; n-- {
		buf = append(buf, '0')
	}
	return buf
}

// 如果x应该向上取整，则应向上报告
// 如果缩短为n位。n必须是x.mant的有效索引
// 。
func shouldRoundUp(x *decimal, n int) bool {
	if x.mant[n] == '5' && n+1 == len(x.mant) {
		// 精确到偶数的中间位
		return n > 0 && (x.mant[n-1]-'0')&1 != 0
	}
	// 非中间位数字表示全部（x.mant没有尾随零）
	return x.mant[n] >= '5'
}

// 四舍五入将x设置为（最多）n个尾数数字
// 四舍五入到最接近的偶数值（或发热）尾数数字。
// 如果n<0，则x保持不变。
func (x *decimal) round(n int) {
	if n < 0 || n >= len(x.mant) {
		return // 无事
	}

	if shouldRoundUp(x, n) {
		x.roundUp(n)
	} else {
		x.roundDown(n)
	}
}

func (x *decimal) roundUp(n int) {
	if n < 0 || n >= len(x.mant) {
		return // 无事
	}
	// 0<=n<len（x.mant）

	// 查找第一个数字<'9'
	for n > 0 && x.mant[n-1] >= '9' {
		n--
	}

	if n == 0 {
		// 所有数字都是'9's=>四舍五入到'1'并更新指数
		x.mant[0] = '1' // 自len（x.mant）>n 
		x.mant = x.mant[:1]
		x.exp++
		return
	}

	// n>0&&x.mant[n-1]<'9'
	x.mant[n-1]++
	x.mant = x.mant[:n]
	// x已修剪
}

func (x *decimal) roundDown(n int) {
	if n < 0 || n >= len(x.mant) {
		return // 
	}
	x.mant = x.mant[:n]
	trim(x)
}

// trim从x的尾数中删除任何尾随的零；
// 它们对于x的值来说没有意义。
func trim(x *decimal) {
	i := len(x.mant)
	for i > 0 && x.mant[i-1] == '0' {
		i--
	}
	x.mant = x.mant[:i]
	if i == 0 {
		x.exp = 0
	}
}
